package com.zebra.gateway.session;

import cn.hutool.core.util.ArrayUtil;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.Scheduler;
import com.zebra.common.concurrent.ZebraThreadFactory;
import io.netty.channel.Channel;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * session 只保存 客户端的session
 * @Date 2020/7/9 0009
 **/
public class SessionManager {

    private static SessionManager sessionManager = new SessionManager();

    private AtomicInteger onlineNumber = new AtomicInteger(0);
    private final Cache<Long, Session> sessionCache = Caffeine.newBuilder()
            .expireAfterAccess(120, TimeUnit.MINUTES)
            .scheduler(Scheduler.forScheduledExecutorService(Executors.newScheduledThreadPool(1, new ZebraThreadFactory("cache-pool"))))
            .removalListener(new SessionExpireProcessor())
            .build();

    private SessionManager() {
    }

    public static SessionManager getInstance() {
        if (null == sessionManager) {
            sessionManager = new SessionManager();
        }
        return sessionManager;
    }

    public Session getSessionByHandler(Channel channel){
        Session session = getSession(channel);
        if (null != session){
            return  session;
        }
        return new Session(channel);
    }

    public void putSession(Session session) {
        if (null == session || session.getUserId() < 1) {
            return;
        }
        sessionCache.put(session.getUserId(), session);
        ZebraAttribute.setSession(session.getChannel(), session);
        onlineNumber.getAndIncrement();
    }


    public Session getSession(Long userId) {
        return sessionCache.getIfPresent(userId);
    }

    public Session getSession(Channel channel){
        return ZebraAttribute.getSession(channel);
    }

    public void removeBySession(Session session) {
        if (null == session || session.getUserId() < 1) {
            return;
        }
        sessionCache.invalidate(session.getUserId());
        ZebraAttribute.removeSession(session.getChannel());
        onlineNumber.getAndDecrement();
    }

    public boolean checkRemoveSession(Session session){
        Session mapSession = sessionCache.getIfPresent(session.getUserId());
        if (null == mapSession || session.getChannel().equals(mapSession.getChannel())){
            return true;
        }
        return false;
    }

    public void  weakClose(long userId){
        if (userId <1){
            return;
        }
        Session session = getSession(userId);
        if (null != session){
            session.setWeakClose(true);
        }
    }


    public void closeChannel(Channel channel) {
        if (channel == null) {
            return;
        }
        try {
            if (channel.isActive() || channel.isOpen()) {
                channel.close().sync();
            }
        } catch (InterruptedException e) {
        }
    }


    public Collection<Session> getAll(){
        return sessionCache.asMap().values();
    }

    public List<Session> getAllFine(){
        return getAll().stream().filter(Session::channelFine).collect(Collectors.toList());
    }

    public List<Session> getGames(int[] gamIds){
        if (null == gamIds || gamIds.length == 0){
            return null;
        }
        if (gamIds.length == 1){
            int gameId = gamIds[0];
            return getAllFine().stream().filter(s -> s.getGameId() == gameId).collect(Collectors.toList());
        }
        return getAllFine().stream().filter(s -> ArrayUtil.contains(gamIds, s.getGameId())).collect(Collectors.toList());
    }


    public List<Session> getPerson(long[] userIds){
        if (null == userIds || userIds.length == 0){
            return null;
        }
        int l = userIds.length;
        List<Session> sessions = new ArrayList<>(userIds.length);
        Session session;
        for (int i=0; i<l; i++){
            session = getSession(userIds[i]);
            if (null != session && session.channelFine()){
                sessions.add(session);
            }
        }
        return sessions;
    }

    public int getOnlineNumber(){
        return onlineNumber.get();
    }


}
